#pragma once
#include "log.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include "log.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <array>
#include <poll.h>
namespace poll_ns
{

    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };

    class PollServer
    {
        // static const uint16_t dport = 20000;
        static const uint16_t defport;
        static const int gbacklog = 20;

    public:
        PollServer(const uint16_t port = defport) : _sock(-1), _port(port), _rfds(nullptr)
        {
        }

        ~PollServer()
        {
            if (_sock > 0)
                close(_sock);
            if (_rfds)
                delete[] _rfds;
        }

        void initServer()
        {
            // 创建套接字
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if (_sock < 0)
            {
                logcout(FATAL, "create socket error !!");
                exit(SOCKET_ERR);
            }
            logcout(NORMAL, "create socket error !!");

            // 设置地址复用  保证立即重启
            int opt = 1;
            setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

            // 绑定bind
            struct sockaddr_in addr;
            addr.sin_port = htons(_port);
            addr.sin_family = AF_INET;
            // addr.sin_family= PF_INET;  一样的

            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            addr.sin_addr.s_addr = INADDR_ANY;

            int ret = bind(_sock, (struct sockaddr *)&addr, sizeof(addr));
            if (ret < 0)
            {
                logcout(FATAL, "bind socket error!!");
                exit(BIND_ERR);
            }
            logcout(NORMAL, "bind socket success!!");

            // 设置监听状态

            ret = listen(_sock, gbacklog);
            if (ret < 0)
            {
                logcout(FATAL, "listen socket error!!");
                exit(LISTEN_ERR);
            }
            logcout(NORMAL, "listen socket success!!");

            _rfds = new struct pollfd[1024];

            // 初始化
            for (int i = 0; i < 1024; i++)
            {
                _rfds[i].fd = -1;
                _rfds[i].events = 0;
                _rfds[i].revents = 0;
            }
            _rfds[0].fd = _sock;
            _rfds[0].events = POLLIN;
        }

        void start()
        {
            int timeout = 1000;
            for (;;)
            {
                int n = poll(_rfds, 1024, timeout);
                std::cout << "poll retval: " << n << std::endl;
                switch (n)
                {
                case 0:
                    /* 超时 */
                    logcout(WARNING, "poll tmieout!! ");
                    break;
                case -1:
                    /* 出错了 */
                    logcout(WARNING, "poll error ");
                    break;
                default:
                    /* sock 事件就绪了 */
                    logcout(NORMAL, "有事件到来 !!");
                    handerEvent();
                    break;
                }
            }
        }

        void handerEvent()
        {
            // handerEvent 其中有多个事件读取就绪
            for (int i = 0; i < 1024; i++)
            {
                // 过滤掉非法的文件描述符
                if (_rfds[i].fd == -1)
                    continue;

                /*监听事件就绪 : 新链接到来*/
                if (_rfds[i].fd == _sock && _rfds[i].revents & POLLIN)
                {
                    /*_sock读取事件就绪 : 新链接到来*/
                    struct sockaddr_in caddr;
                    socklen_t len = sizeof(caddr); // 这里是 值-结果参数
                    int newsock = accept(_sock, (struct sockaddr *)&caddr, &len);
                    // 返回一个新的文件描符号 用于通信
                    if (newsock < 0)
                    {
                        logcout(ERROR, "accept a new link error!!");
                    }
                    logcout(NORMAL, "accept a new link success!!");
                    std::cout << "accept sock : " << newsock << std::endl;
                    // 到这里 lisent 的事件处理完成
                    // 读取的链接之后的新的newsock 应该托管给select
                    //  一般而言 要用select, 程序员需要自己维护一个合法的fd数组

                    // 将新的newsock 添加到_rfds数组中
                    int i = 0;
                    for (i = 0; i < 1024; i++)
                    {
                        if (_rfds[i].fd != -1)
                            continue;
                        else
                            break;
                    }
                    if (i == 1024)
                    {
                        logcout(WARNING, "poll if full please wait!!");
                        close(newsock);
                    }
                    else
                    {
                        _rfds[i].fd = newsock;
                        _rfds[i].events = POLLIN;
                        _rfds[i].revents = 0;
                    }
                    logcout(NORMAL, "newsock add _rfds!!");
                }
                else if ((_rfds[i].events & POLLIN) && (_rfds[i].revents & POLLIN))
                {
                    // 正常的通信事件
                    Recver(_rfds[i].fd, i);
                }
                else
                    continue;
            }
        }

        void Recver(int sock, int pos)
        {
            // 到这里的读取事件
            char buf[1024];                                  // 这里读取的不一定是 读取一个完整的协议 ,所以要打while循环,
            ssize_t n = recv(sock, buf, sizeof(buf) - 1, 0); // 这里不会被阻塞, 但是在 while循环中第二次读取的时候 不一定不会被阻塞
            if (n > 0)
            {
                buf[n] = 0;
                std::cout << "server 读取到一个消息: " << buf << std::endl;

                std::string str = buf;
                send(sock, str.c_str(), str.size(), 0);
            }
            else if (n == 0)
            {
                close(sock);
                _rfds[pos].fd = -1;
                _rfds[pos].events = 0;
                _rfds[pos].revents = 0;
                logcout(NORMAL, "client quit!!");
            }
            else
            {
                close(sock);
                _rfds[pos].fd = -1;
                _rfds[pos].events = 0;
                _rfds[pos].revents = 0;
                logcout(ERROR, "recv error");
            }
        }

    private:
        uint16_t _port;
        std::string _ip;
        int _sock;
        struct pollfd *_rfds;
    };

    const uint16_t PollServer::defport = 20000;
    // const int SelectServer::gbacklog = 20;
}
